// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart = 2.8

import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mxflutter/mxflutter.dart';
import 'package:mxflutter/mxflutter_test.dart';
import 'package:mxflutter/src/mirror_reg_fun_map/mx_mirror_func_reg.dart';

void approxExpect(Alignment a, Alignment b) {
  expect(a.x, moreOrLessEquals(b.x));
  expect(a.y, moreOrLessEquals(b.y));
}

void main() {
  setUp(() {
    mxRegisterMirrorFunctions();
  });
  dynamic widgetFromJson(Map<String, dynamic> widgetJson) {
    // 这里先统一使用root 后续要考虑怎么生成一个合适的owner
    MXBuildOwner owner = MXBuildOwner.rootBuildOwner();
    dynamic widget =
        MXMirror.getInstance().jsonToDartObj(widgetJson, buildOwner: owner);
    return widget;
  }

  test('Alignment control test', () {
    final Alignment alignment = widgetFromJson({
      'className': 'Alignment',
      'x': 0.5,
      'y': 0.25
    }) as Alignment;

    expect(alignment, hasOneLineDescription);
    expect(alignment.hashCode, equals(const Alignment(0.5, 0.25).hashCode));

    expect(alignment / 2.0, const Alignment(0.25, 0.125));
    expect(alignment ~/ 2.0, const Alignment(0.0, 0.0));
    expect(alignment % 5.0, const Alignment(0.5, 0.25));
  });

  test('Alignment.lerp()', () {
    final Alignment a = widgetFromJson({
      'className': 'Alignment',
      'constructorName': 'topLeft'
    }) as Alignment;
    final Alignment b = widgetFromJson({
      'className': 'Alignment',
      'constructorName': 'topCenter'
    }) as Alignment;
    expect(Alignment.lerp(a, b, 0.25), equals(const Alignment(-0.75, -1.0)));

    expect(Alignment.lerp(null, null, 0.25), isNull);
    expect(Alignment.lerp(null, b, 0.25), equals(const Alignment(0.0, -0.25)));
    expect(Alignment.lerp(a, null, 0.25), equals(const Alignment(-0.75, -0.75)));
  });

  test('AlignmentGeometry invariants', () {
    final AlignmentDirectional topStart = widgetFromJson({
      'className': 'AlignmentDirectional',
      'constructorName': 'topStart'
    }) as AlignmentDirectional;
    final AlignmentDirectional topEnd = widgetFromJson({
      'className': 'AlignmentDirectional',
      'constructorName': 'topEnd'
    }) as AlignmentDirectional;
    final Alignment center = widgetFromJson({
      'className': 'Alignment',
      'constructorName': 'center'
    }) as Alignment;
    final Alignment topLeft = widgetFromJson({
      'className': 'Alignment',
      'constructorName': 'topLeft'
    }) as Alignment;
    final Alignment topRight = widgetFromJson({
      'className': 'Alignment',
      'constructorName': 'topRight'
    }) as Alignment;
    final List<double> numbers = <double>[0.0, 1.0, -1.0, 2.0, 0.25, 0.5, 100.0, -999.75];

    expect((topEnd * 0.0).add(topRight * 0.0), center);
    expect(topEnd.add(topRight) * 0.0, (topEnd * 0.0).add(topRight * 0.0));
    expect(topStart.add(topLeft), topLeft.add(topStart));
    expect((topStart.add(topLeft)).resolve(TextDirection.ltr), (topStart.resolve(TextDirection.ltr)) + topLeft);
    expect((topStart.add(topLeft)).resolve(TextDirection.rtl), (topStart.resolve(TextDirection.rtl)) + topLeft);
    expect((topStart.add(topLeft)).resolve(TextDirection.ltr), (topStart.resolve(TextDirection.ltr)).add(topLeft));
    expect((topStart.add(topLeft)).resolve(TextDirection.rtl), (topStart.resolve(TextDirection.rtl)).add(topLeft));
    expect(topStart.resolve(TextDirection.ltr), topLeft);
    expect(topStart.resolve(TextDirection.rtl), topRight);
    expect(topEnd * 0.0, center);
    expect(topLeft * 0.0, center);
    expect(topStart * 1.0, topStart);
    expect(topEnd * 1.0, topEnd);
    expect(topLeft * 1.0, topLeft);
    expect(topRight * 1.0, topRight);
    for (final double n in numbers) {
      expect((topStart * n).add(topStart), topStart * (n + 1.0));
      expect((topEnd * n).add(topEnd), topEnd * (n + 1.0));
      for (final double m in numbers)
        expect((topStart * n).add(topStart * m), topStart * (n + m));
    }
    expect(topStart + topStart + topStart, topStart * 3.0); // without using "add"
    for (final TextDirection x in TextDirection.values) {
      expect((topEnd * 0.0).add(topRight * 0.0).resolve(x), center.add(center).resolve(x));
      expect((topEnd * 0.0).add(topLeft).resolve(x), center.add(topLeft).resolve(x));
      expect(((topEnd * 0.0).resolve(x)).add(topLeft.resolve(x)), (center.resolve(x)).add(topLeft.resolve(x)));
      expect(((topEnd * 0.0).resolve(x)).add(topLeft), (center.resolve(x)).add(topLeft));
      expect((topEnd * 0.0).resolve(x), center.resolve(x));
    }
    expect(topStart, isNot(topLeft));
    expect(topEnd, isNot(topLeft));
    expect(topStart, isNot(topRight));
    expect(topEnd, isNot(topRight));
    expect(topStart.add(topLeft), isNot(topLeft));
    expect(topStart.add(topLeft), isNot(topStart));
  });

  test('AlignmentGeometry.resolve()', () {
  //  final AlignmentDirectional center = ;
    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 0.25,
      'y': 0.3,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.ltr',
      'index': 1,
    }))), const Alignment(0.25, 0.3));
    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 0.25,
      'y': 0.3,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.rtl',
      'index': 0,
    }))), const Alignment(-0.25, 0.3));
    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': -0.25,
      'y': 0.3,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.ltr',
      'index': 1,
    }))), const Alignment(-0.25, 0.3));
    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': -0.25,
      'y': 0.3,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.rtl',
      'index': 0,
    }))), const Alignment(0.25, 0.3));

    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 1.25,
      'y': 0.3,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.ltr',
      'index': 1,
    }))), const Alignment(1.25, 0.3));
    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 1.25,
      'y': 0.3,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.rtl',
      'index': 0,
    }))), const Alignment(-1.25, 0.3));

    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 0.5,
      'y': -0.3,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.ltr',
      'index': 1,
    }))), const Alignment(0.5, -0.3));
    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 0.5,
      'y': -0.3,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.rtl',
      'index': 0,
    }))), const Alignment(-0.5, -0.3));

    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 0.0,
      'y': 0.0,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.ltr',
      'index': 1,
    }))), const Alignment(0.0, 0.0));
    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 0.0,
      'y': 0.0,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.rtl',
      'index': 0,
    }))), const Alignment(0.0, 0.0));

    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 1.0,
      'y': 1.0,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.ltr',
      'index': 1,
    }))), const Alignment(1.0, 1.0));
    expect((widgetFromJson({
      'className': 'AlignmentDirectional',
      'start': 1.0,
      'y': 1.0,
    }) as AlignmentDirectional).resolve((widgetFromJson({
      '_name': 'TextDirection.rtl',
      'index': 0,
    }))), const Alignment(-1.0, 1.0));

    // expect(AlignmentDirectional(nonconst(1.0), 2.0), AlignmentDirectional(nonconst(1.0), 2.0));
    // expect(const AlignmentDirectional(1.0, 2.0), isNot(const AlignmentDirectional(2.0, 1.0)));
    // expect(const AlignmentDirectional(-1.0, 0.0).resolve(TextDirection.ltr),
    //        const AlignmentDirectional(1.0, 0.0).resolve(TextDirection.rtl));
    // expect(const AlignmentDirectional(-1.0, 0.0).resolve(TextDirection.ltr),
    //  isNot(const AlignmentDirectional(1.0, 0.0).resolve(TextDirection.ltr)));
    // expect(const AlignmentDirectional(1.0, 0.0).resolve(TextDirection.ltr),
    //  isNot(const AlignmentDirectional(1.0, 0.0).resolve(TextDirection.rtl)));
  });

  // test('AlignmentGeometry.lerp ad hoc tests', () {
  //   final AlignmentGeometry mixed1 = const Alignment(10.0, 20.0).add(const AlignmentDirectional(30.0, 50.0));
  //   final AlignmentGeometry mixed2 = const Alignment(70.0, 110.0).add(const AlignmentDirectional(130.0, 170.0));
  //   final AlignmentGeometry mixed3 = const Alignment(25.0, 42.5).add(const AlignmentDirectional(55.0, 80.0));

  //   for (final TextDirection direction in TextDirection.values) {
  //     expect(AlignmentGeometry.lerp(mixed1, mixed2, 0.0).resolve(direction), mixed1.resolve(direction));
  //     expect(AlignmentGeometry.lerp(mixed1, mixed2, 1.0).resolve(direction), mixed2.resolve(direction));
  //     expect(AlignmentGeometry.lerp(mixed1, mixed2, 0.25).resolve(direction), mixed3.resolve(direction));
  //   }
  // });

  // test('lerp commutes with resolve', () {
  //   final List<AlignmentGeometry> offsets = <AlignmentGeometry>[
  //     Alignment.topLeft,
  //     Alignment.topCenter,
  //     Alignment.topRight,
  //     AlignmentDirectional.topStart,
  //     AlignmentDirectional.topCenter,
  //     AlignmentDirectional.topEnd,
  //     Alignment.centerLeft,
  //     Alignment.center,
  //     Alignment.centerRight,
  //     AlignmentDirectional.centerStart,
  //     AlignmentDirectional.center,
  //     AlignmentDirectional.centerEnd,
  //     Alignment.bottomLeft,
  //     Alignment.bottomCenter,
  //     Alignment.bottomRight,
  //     AlignmentDirectional.bottomStart,
  //     AlignmentDirectional.bottomCenter,
  //     AlignmentDirectional.bottomEnd,
  //     const Alignment(-1.0, 0.65),
  //     const AlignmentDirectional(-1.0, 0.45),
  //     const AlignmentDirectional(0.125, 0.625),
  //     const Alignment(0.25, 0.875),
  //     const Alignment(0.0625, 0.5625).add(const AlignmentDirectional(0.1875, 0.6875)),
  //     const AlignmentDirectional(2.0, 3.0),
  //     const Alignment(2.0, 3.0),
  //     const Alignment(2.0, 3.0).add(const AlignmentDirectional(5.0, 3.0)),
  //     const Alignment(10.0, 20.0).add(const AlignmentDirectional(30.0, 50.0)),
  //     const Alignment(70.0, 110.0).add(const AlignmentDirectional(130.0, 170.0)),
  //     const Alignment(25.0, 42.5).add(const AlignmentDirectional(55.0, 80.0)),
  //     null,
  //   ];

  //   final List<double> times = <double>[ 0.25, 0.5, 0.75 ];

  //   for (final TextDirection direction in TextDirection.values) {
  //     final Alignment defaultValue = AlignmentDirectional.center.resolve(direction);
  //     for (final AlignmentGeometry a in offsets) {
  //       final Alignment resolvedA = a?.resolve(direction) ?? defaultValue;
  //       for (final AlignmentGeometry b in offsets) {
  //         final Alignment resolvedB = b?.resolve(direction) ?? defaultValue;
  //         approxExpect(Alignment.lerp(resolvedA, resolvedB, 0.0), resolvedA);
  //         approxExpect(Alignment.lerp(resolvedA, resolvedB, 1.0), resolvedB);
  //         approxExpect((AlignmentGeometry.lerp(a, b, 0.0) ?? defaultValue).resolve(direction), resolvedA);
  //         approxExpect((AlignmentGeometry.lerp(a, b, 1.0) ?? defaultValue).resolve(direction), resolvedB);
  //         for (final double t in times) {
  //           assert(t > 0.0);
  //           assert(t < 1.0);
  //           final Alignment value = (AlignmentGeometry.lerp(a, b, t) ?? defaultValue).resolve(direction);
  //           approxExpect(value, Alignment.lerp(resolvedA, resolvedB, t));
  //           final double minDX = math.min(resolvedA.x, resolvedB.x);
  //           final double maxDX = math.max(resolvedA.x, resolvedB.x);
  //           final double minDY = math.min(resolvedA.y, resolvedB.y);
  //           final double maxDY = math.max(resolvedA.y, resolvedB.y);
  //           expect(value.x, inInclusiveRange(minDX, maxDX));
  //           expect(value.y, inInclusiveRange(minDY, maxDY));
  //         }
  //       }
  //     }
  //   }
  // });

  // test('AlignmentGeometry add/subtract', () {
  //   const AlignmentGeometry directional = AlignmentDirectional(1.0, 2.0);
  //   const AlignmentGeometry normal = Alignment(3.0, 5.0);
  //   expect(directional.add(normal).resolve(TextDirection.ltr), const Alignment(4.0, 7.0));
  //   expect(directional.add(normal).resolve(TextDirection.rtl), const Alignment(2.0, 7.0));
  //   expect(normal.add(normal), normal * 2.0);
  //   expect(directional.add(directional), directional * 2.0);
  // });

  // test('AlignmentGeometry operators', () {
  //   expect(const AlignmentDirectional(1.0, 2.0) * 2.0, const AlignmentDirectional(2.0, 4.0));
  //   expect(const AlignmentDirectional(1.0, 2.0) / 2.0, const AlignmentDirectional(0.5, 1.0));
  //   expect(const AlignmentDirectional(1.0, 2.0) % 2.0, const AlignmentDirectional(1.0, 0.0));
  //   expect(const AlignmentDirectional(1.0, 2.0) ~/ 2.0, const AlignmentDirectional(0.0, 1.0));
  //   for (final TextDirection direction in TextDirection.values) {
  //     expect(Alignment.center.add(const AlignmentDirectional(1.0, 2.0) * 2.0).resolve(direction), const AlignmentDirectional(2.0, 4.0).resolve(direction));
  //     expect(Alignment.center.add(const AlignmentDirectional(1.0, 2.0) / 2.0).resolve(direction), const AlignmentDirectional(0.5, 1.0).resolve(direction));
  //     expect(Alignment.center.add(const AlignmentDirectional(1.0, 2.0) % 2.0).resolve(direction), const AlignmentDirectional(1.0, 0.0).resolve(direction));
  //     expect(Alignment.center.add(const AlignmentDirectional(1.0, 2.0) ~/ 2.0).resolve(direction), const AlignmentDirectional(0.0, 1.0).resolve(direction));
  //   }
  //   expect(const Alignment(1.0, 2.0) * 2.0, const Alignment(2.0, 4.0));
  //   expect(const Alignment(1.0, 2.0) / 2.0, const Alignment(0.5, 1.0));
  //   expect(const Alignment(1.0, 2.0) % 2.0, const Alignment(1.0, 0.0));
  //   expect(const Alignment(1.0, 2.0) ~/ 2.0, const Alignment(0.0, 1.0));
  // });

  // test('AlignmentGeometry operators', () {
  //   expect(const Alignment(1.0, 2.0) + const Alignment(3.0, 5.0), const Alignment(4.0, 7.0));
  //   expect(const Alignment(1.0, 2.0) - const Alignment(3.0, 5.0), const Alignment(-2.0, -3.0));
  //   expect(const AlignmentDirectional(1.0, 2.0) + const AlignmentDirectional(3.0, 5.0), const AlignmentDirectional(4.0, 7.0));
  //   expect(const AlignmentDirectional(1.0, 2.0) - const AlignmentDirectional(3.0, 5.0), const AlignmentDirectional(-2.0, -3.0));
  // });

  // test('AlignmentGeometry toString', () {
  //   expect(const Alignment(1.0001, 2.0001).toString(), 'Alignment(1.0, 2.0)');
  //   expect(const Alignment(0.0, 0.0).toString(), 'center');
  //   expect(const Alignment(-1.0, 1.0).add(const AlignmentDirectional(1.0, 0.0)).toString(), 'bottomLeft + AlignmentDirectional.centerEnd');
  //   expect(const Alignment(0.0001, 0.0001).toString(), 'Alignment(0.0, 0.0)');
  //   expect(const Alignment(0.0, 0.0).toString(), 'center');
  //   expect(const AlignmentDirectional(0.0, 0.0).toString(), 'AlignmentDirectional.center');
  //   expect(const Alignment(1.0, 1.0).add(const AlignmentDirectional(1.0, 1.0)).toString(), 'Alignment(1.0, 2.0) + AlignmentDirectional.centerEnd');
  // });
}
